package com.cable.utils.db;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FieldUtils {

	public static <T, R> Object getValue(Map map,FieldGetter<T, R> fieldGetter) {
		return map.get(getFieldName(fieldGetter));
	}

	public static <T, R> Field getField(FieldGetter<T, R> fieldGetter) {
		return fieldGetter.getField();
	}

	public static <T, R> String getFieldName(FieldGetter<T, R> fieldGetter) {
		Field field = getField(fieldGetter);
		return field != null ? field.getName() : "";
	}

	public static <T, R> Class getSerializedClass(FieldGetter<T, R> fieldGetter) {
		return fieldGetter.getSerializedClass();
	}

	public static Field getFieldFromLambda(Serializable lambda) {
		Field field = null;
		try {
			Method method = ReflectUtil.getMethodByName(lambda.getClass(), "writeReplace");
			method.setAccessible(true);
			SerializedLambda serializedLambda = (SerializedLambda) method.invoke(lambda);
			String methodName = serializedLambda.getImplMethodName();
			if (methodName.startsWith("get")) {
				methodName = methodName.substring(3);
			} else if (methodName.startsWith("is")) {
				methodName = methodName.substring(2);
			}
			// 首字母变小写
			methodName = CharSequenceUtil.lowerFirst(methodName);

			field = ReflectUtil.getField(
				Class.forName(serializedLambda.getImplClass().replace("/", ".")), methodName);
		} catch (Exception e) {
			log.error("", e);
		}
		return field;
	}

	public interface FieldGetter<T, R> extends Function<T, R>, Serializable {

		default Field getField() {
			return 	FieldUtils.getFieldFromLambda(this);
		}

		default Class getSerializedClass() {
			Class clazz = null;

			try {
				Method method = ReflectUtil.getMethodByName(this.getClass(), "writeReplace");
				method.setAccessible(true);
				SerializedLambda serializedLambda = (SerializedLambda) method.invoke(this);
				String className = serializedLambda.getImplClass();

				clazz = Class.forName(className.replace("/", "."));
			} catch (Exception e) {
				log.error("", e);
			}

			return clazz;
		}

	}

}
